Cesium VoxelPrimitive 实现体渲染
Cesium
1.101
版本新增了 VoxelPrimitive
类和 VoxelProvider
接口,专门用于体素渲染,同时支持提体积渲染。
这项技术已经推出 2 年了,但目前官方文档中仍标记为实验特性(Experimental),也许是因为这样,国内才鲜有资料详细推广这项技术。本文结合代码详细介绍如何使用 VoxelPrimitive
、VoxelProvider
和 CustomShader
实现体素渲染和体积渲染。
体积渲染在气象数据可视化中的应用
1、体素渲染与体积渲染
体素渲染(Voxel Rendering)是一种通过体素(Voxel,即体积像素)来进行图像渲染的技术。与传统的基于表面的渲染技术不同,体素渲染通过将三维空间分割成小的立方体单元来表示物体,这样可以更精确地捕捉复杂的内部结构和细节。
体素渲染效果(图由 AI 生成)
体积渲染(Volume Rendering)是一种在三维空间中显示物体内部结构的图像渲染技术。与体素渲染类似,体积渲染能够展示复杂的内部细节,但它不局限于立方体单元,而是能够渲染连续的三维数据。
天气雷达体积渲染效果
2、二三维格点数据
二维格点数据(2D Grid Data)是在二维空间中以规则网格形式排列的数据点。每个数据点对应一个特定的位置(通常是以行列表示)和属性值。这种数据形式广泛应用于科学计算、地理信息系统(GIS)、图像处理等领域。
二维格点数据可视化效果(全球二氧化碳浓度分布)
三维格点数据(3D Grid Data)是指在三维空间中以规则网格形式排列的数据点,每个数据点对应一个特定的位置和属性值。这种数据形式常用于科学计算、地质勘探、气象模拟等领域,可以高效地表示连续的三维分布信息。应用场景:
气象模拟:用于表示大气中的各种参数,如温度、压力、湿度等。
医学成像:如 CT 和 MRI 数据,以三维格点形式展示人体内部结构。
地质勘探:用于表示地下矿藏分布、地形等信息。
计算流体力学:用于模拟流体的流动状态,如空气动力学分析等。
三维格点数据可视化效果(二氧化碳浓度垂直分布)
3、Cesium 体渲染技术
我们先通过一张类图对 Cesium 体渲染技术有个总体的认识。
Cesium 体素渲染技术体系类图(精简版)
Cesium 体渲染技术体系主要由 VoxelPrimitive
类、 VoxelProvider
接口和一系列枚举类型组成,通过 Cesium 自定义着色器 CustomShader
实现体素数据(如三维格点数据)到颜色的映射。
3.1、体素数据源 VoxelProvider
我们需要自定义数据源类,实现体素数据源接口 VoxelProvider
,包括如下几个方面:
通过
shape
属性定义整体形状;通过
dimensions
属性定义体素切片的数据维度;通过
names
、types
和componentTypes
属性定义体素元数据;编写
requestData
方法,处理体素切片数据请求,返回体素数据。
3.1.1、整体形状定义
截至本文编写时间,Cesium 最新版为 1.123, 支持立方体、椭球体和圆柱体三种整体形状的体素渲染。从类图可以看出,属性 shape
可选值如下:
VoxelShapeType.BOX
整体形状为立方体,边界使用局部空间的直角坐标表示,通常适用于小范围的局部空间三维格点数据,需要注意的是,离中心位置越远的点,高度误差越大,当东西或者南北的距离超过50公里
时请考虑使用椭球体;VoxelShapeType.ELLIPSOID
整体形状为椭球体,边界使用球坐标表示,一般适用于空间范围较大的三维格点数据,同时这种形状的精度抖动较大,相机拉近到边缘时会出现明显的抖动;VoxelShapeType.CYLINDER
整体形状为圆柱体,目前还不清楚具体应用场景。
体素渲染的3种整体形状效果图
3.1.2、体素切片定义
体素切片和二维影像切片类似,是把1
个体块切割成8
个大小相同体块,其中任一体块我们称之为体素切片。任一个体素切片包含的体素数量相同,这个数量由 dimensions
属性指定。
体素切片示意图(左:Level 1,右:Level 2)
体素切片的数据维度
Cesium 通过纹理将体素数据传入 GPU,受到纹理大小的限制,每个体素切片的体素数据量是有严格限制的。初步测试表明,在只有一个元数据,且元数据类型为
VEC4
、元数据分量类型为FLOAT32
的情况下,1
个体素切片的数据维度最大值为(157,157,157)
。这个值仅供参考,实际应用中,其中 1 个维度大小调低、另外 2 个维度就可以适当调高。
3.1.2、体素元数据定义
体素元数据(Voxel Metadata)是指与体素(Voxel,即体积像素)相关的附加信息,这些信息用于描述体素的属性、状态或其他相关特征,例如颜色、透明度或者大气温度、二氧化碳浓度等等。
一个体素可以包含多个元数据,一个元数据包含名称、数据类型、数值 3 个方面的信息。由于体素渲染在 GPU 中完成,我们需要将数据通过更利于在 CPU 和 GPU 之间高效传输的方式进行组织。
VoxelProvider
通过names
、types
、componentTypes
定义体素元数据,这三个数组的长度必须一致。
names
元数据名称数组,这个名称在fragmentShader
中通过fsInput.metadata.<name>
的方式获取,例如fsInput.metadata.color;
,types
元数据类型数组,例如颜色元数据类型为三维向量或四维向量,透明度为浮点型。这个属性在编写CustomShader
的fragmentShader
时候需要关注;componentTypes
元数据分量类型,如颜色的四个分量可以转为0~255
范围,然后使用UINT8
类型传输,数据传输时交换的数据量相对小一些。这个属性在编写requestData
方法时候需要关注。
3.1.3、体素切片数据请求
Cesium 内部实现了整套体素切片调度流程,自动根据相机距离决定需要渲染的 LOD 层级和体素切片,我们主要工作是实现VoxelProvider
接口方法requestData
,响应每个切片的数据请求,返回准备就绪的切片数据,如果返回undefined
则不显示该切片,也不会继续进行细分。
返回的切片数据数组的第一个维度与元数据定义的names
数组长度必须一致,第二个维度必须等于dimensions.x * dimensions.y * dimensions.z * channelCount
。
这里channelCount
由元数据分量类型(componentTypes
)决定,例如VEC4
类型即四维向量的channelCount
为4
,VEC3
为3
,SCALAR
等为1
,Cesium 提供了一个获取channelCount
的方法Cesium.MetadataType.getComponentCount
。
一个简单示例:
class ProceduralMultiTileVoxelProvider { constructor(shape) { this.shape = shape; this.dimensions = new Cesium.Cartesian3(4, 4, 4); this.names = ["color", "alpha"]; this.types = [Cesium.MetadataType.VEC4, Cesium.MetadataType.SCALAR]; this.componentTypes = [ Cesium.MetadataComponentType.UINT8, Cesium.MetadataComponentType.FLOAT32, ]; this._levelCount = 2; } requestData(options) { const { tileLevel, tileX, tileY, tileZ } = options; if (tileLevel >= this._levelCount) { return undefined; } const dimensions = this.dimensions; //元数据:color const colorMetadataType = this.types[0]; const voxelCount = dimensions.x * dimensions.y * dimensions.z; const colorChannelCount = Cesium.MetadataType.getComponentCount(colorMetadataType); const dataColor = new Uint8Array(voxelCount * colorChannelCount).fill(1); //元数据:alpha const alphaMetadataType = this.types[1]; const alphaChannelCount = Cesium.MetadataType.getComponentCount(alphaMetadataType); const dataAlpha = new Float32Array(voxelCount * alphaChannelCount).fill(1); //按照 names 数组的顺序排列 return Promise.resolve([dataColor, dataAlpha]); }}
3.2、体素着色 CustomShader
数据源类已经定义了每个体素的元数据,接下来使用 Cesium 自定义着色器接收体素数据,并转为颜色和透明度,通过 czm_modelMaterial
类型的材质对象的diffuse
和alpha
字段传入 Cesium 内置的 RayMarching
算法流程,完成最终的体素数据渲染。
const customShader = new Cesium.CustomShader({ fragmentShaderText: `void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) { vec3 cellColor = fsInput.metadata.color.rgb; //获取元数据 color vec3 cellAlpha = fsInput.metadata.color.alpha; //获取元数据 alpha //TODO: 如果元数据不是颜色,则在这里继续实现从获取元数据到颜色的转换处理 material.diffuse = cellColor; material.alpha = cellAlpha; }`,});
这个示例运行结果是所有体素都是白色,没有任何实际用处,仅展示数据的从VoxelProvider
到CustomShader
的整体处理流程。
3.3、体素图元 VoxelPrimitive
前面准备工作完成,接下来就比较容易了。创建 VoxelPrimitive
,传入 provider
、customShader
、modelMatrix
,然后添加到viewer.scene.primitives
即可。
const provider = new ProceduralMultiTileVoxelProvider( Cesium.VoxelShapeType.BOX);const voxelPrimitive = scene.primitives.add( new Cesium.VoxelPrimitive({ provider: provider, customShader: customShader, modelMatrix: modelMatrix, }));
VoxelPrimitive
重要参数有:
modelMatrix
空间变换矩阵,用于空间定位和调整整体大小等;screenSpaceError
LOD 参数,用于调整同一视角下,LOD 的细分程度,值越大细分越粗糙,性能越高,反正越精细,性能越低;nearestSampling
用于切换插值方法,值为 true 则启用邻近插值法,false 则使用线性插值。stepSize
光线步进步长。步长越大,画质越低,性能越高,反之画质越高,性能越低。
3.3.1、空间变换 modelMatrix
无论形状是椭球、立方体,还是圆柱体,Cesium 内部都将整体视为顶点坐标在[-1,1]
范围内的几何体,坐标系在几何体的中心。弄清楚这点,我们才能准确地构造空间变换矩阵,将体素按照指定缩放倍数渲染到目标空间范围内。
空间变换-局部参考系(以立方体形状为例)
示例一
const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame( Cesium.Cartesian3.fromDegrees(106.642372689378, 26.623450331223));Cesium.Matrix4.multiplyByTranslation( modelMatrix, new Cesium.Cartesian3(0, 0, 1000), modelMatrix);const scaleMatrix = Cesium.Matrix4.fromScale( new Cesium.Cartesian3(1000, 1000, 1000));Cesium.Matrix4.multiply(modelMatrix, scaleMatrix, modelMatrix);Cesium.Matrix4.multiplyByTranslation( modelMatrix, new Cesium.Cartesian3(0, 0, 1), modelMatrix);
示例二
const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame( Cesium.Cartesian3.fromDegrees(106.642372689378, 26.623450331223));const localTransformParams = new Cesium.TranslationRotationScale( new Cesium.Cartesian3(0, 0, 1000), null, new Cesium.Cartesian3(1000, 1000, 1000));const localTransform = Cesium.Matrix4.fromTranslationRotationScale(localTransformParams);Cesium.Matrix4.multiply(modelMatrix, localTransform, modelMatrix);
体素图元空间变换
3.3.2、LOD 参数 screenSpaceError
同一视角下两个 screenSpaceError 值效果对比
3.3.3、插值方法 nearestSampling
两种插值方法对比(左:邻近插值,右:线性插值)
4、Cesium 体渲染应用
三维体素数据集通常比较大,Cesium 体渲染通过 LOD(Level of Detail,细节层次),支持大量数据,提高渲染性能。要真正应用这项技术,必须具备 LOD 的数据处理思维,能够结合实际三维格点数据特点,设计出高效的格点数据切片和采样程序。
体积渲染在气象数据可视化中的应用
本文转自 https://mp.weixin.qq.com/s/IbDcjLL-A35ImOVa2QlZ7A,如有侵权,请联系删除。